//
//  config.c
//  myvpn
//
//  Created by Zhuobin He on 13-1-10.
//  Copyright (c) 2013年 Zhuobin He. All rights reserved.
//
#include "SysLib.h"

#include "Const.h"
#include "Config.h"
#include "Common.h"
#include "Firewall.h"

int get_line_type(char *line) {
    if (strlen(trim(line)) == 0) {
        return EMPTY_LINE;
    }
    if (line[0] == '#') {
        return EMPTY_LINE;
    }
    if (line[0] == '[' && line[strlen(line)-1] == ']') {
        return HEADER_START;
    }
    if (strchr(line, '=') != NULL) {
        return KEY_VALUE;
    }
    return UNKNOWN_LINE;
}

void parse_key_value(char *line, char *key, char *value) {
    char *i = strchr(line, '=');
    strncpy(key, line, i - line);
    key[i-line] = 0;
    trim(key);
    strcpy(value, i + 1);
    trim(value);
}

void parse_header_value(char *line, char *value) {
    strcpy(value, line + 1);
    value[strlen(value) - 1] = 0;
    trim(value);
}


PUserInfo read_user_config(FILE *conf_file, char *buffer) {
    PUserInfo user = NULL;
    zmalloc(&user, sizeof(UserInfo));
    bool error = false;
    char key[100], value[100];
    for (;!error;) {
        if (NULL == fgets(buffer, 1023, conf_file)) {
            return user;
        }
        
        switch (get_line_type(buffer)) {
            case HEADER_START:
            {
                return user;
            }
            case KEY_VALUE:
            {
                parse_key_value(buffer, key, value);
                if (strcmp("username", key) == 0) {
                    user->username = string_alloc(value);
                } else if (strcmp("password", key) == 0) {
                    user->password = string_alloc(value);
                } else if (strcmp("script", key) == 0) {
                    user->script = string_alloc(value);
                } else {
                    error = true;
                    break;
                }
                break;
            }
        }
    }

    user_info_delete(user);
    return NULL;
}

PVpnConfig read_base_config(FILE *conf_file, char *buffer) {
    PVpnConfig ret = NULL;
    zmalloc(&ret, sizeof(VpnConfig));
    ret->port = 15044;
    ret->read_buffer_size = 1024 * 1024;
    ret->write_buffer_size = 1024 * 1024;
    ret->renew_interval = 300;

    bool error = false;
    char key[100], value[100];

    for (;!error;) {
        if (NULL == fgets(buffer, 1023, conf_file)) {
            return ret;
        }
        
        switch (get_line_type(buffer)) {
            case HEADER_START:
            {
                return ret;
            }
            case KEY_VALUE:
            {
                parse_key_value(buffer, key, value);
                if (strcmp("mode", key) == 0) {
                    ret->mode = str2int(value);
                } else if (strcmp("host", key) == 0) {
                    ret->host = string_alloc(value);
                } else if (strcmp("dev", key) == 0) {
                    ret->dev = string_alloc(value);
                } else if (strcmp("network", key) == 0) {
                    ret->network = string_alloc(value);
                } else if (strcmp("port", key) == 0) {
                    ret->port = str2int(value);
                } else if (strcmp("log_file", key) == 0) {
                    ret->log_file = string_alloc(value);
                } else if (strcmp("log_level", key) == 0) {
                    ret->log_level = str2int(value);
                } else if (strcmp("renew_interval", key) == 0) {
                    ret->renew_interval = str2int(value);
                } else if (strcmp("read_buffer", key) == 0) {
                    ret->read_buffer_size = str2int(value);
                } else if (strcmp("write_buffer", key) == 0) {
                    ret->write_buffer_size = str2int(value);
                } else if (strcmp("route_cache_file", key) == 0) {
                    ret->route_cache_file = string_alloc(value);
                } else if (strcmp("route_conf_ver", key) == 0) {
                    ret->route_conf_ver = str2int(value);
                } else if (strcmp("tcp_ports", key) == 0) {
                    ret->tcp_ports = string_alloc(value);
                    do_init_firewall_ports(value, IP_PROTO_TCP);
                } else if (strcmp("udp_ports", key) == 0) {
                    ret->udp_ports = string_alloc(value);
                    do_init_firewall_ports(value, IP_PROTO_UDP);
                } else if (strcmp("state_file", key) == 0) {
                    ret->state_file = string_alloc(value);
                } else {
                    error = true;
                    break;
                }
                break;
            }
        }
    }

    vpn_config_delete(ret);
    return NULL;
}


PRoute * do_read_routes(FILE *file, char* buffer) {
    PRoute *routes = NULL;
    zmalloc(&routes, sizeof(PRoute) * 10000);
    int status = 0;
    int i = 0;
    for (;!status;) {
        if (NULL == fgets(buffer, 1023, file)) {
            status = 1;
            break;
        }

        switch (get_line_type(buffer)) {
            case EMPTY_LINE:
            {
                break;
            }
            case UNKNOWN_LINE:
            {
                char *pp = strchr(buffer, '/');
                PRoute route = (PRoute) malloc(sizeof(Route));
                if (pp) {
                    pp[0] = '\0';
                    route->prefix = str2int(pp + 1);
                } else {
                    route->prefix = 32;
                }
                route->address = string_alloc(buffer);
                routes[i++] = route;
                break;
            }
            default:
            {
                status = 1;
                break;
            }
        }
    }
    PRoute *ret = NULL;
    if (status >= 0) {
        zmalloc(&ret, sizeof(PRoute) * (i + 1));
        memcpy(ret, routes, sizeof(PRoute) * i);
    }
    free(routes);
    return ret;
}

PVpnConfig read_config(const char *path) {
    PVpnConfig vpnConfig = NULL;
    PUserInfo users[256];
    int user_index = 0;
    PRoute *routes = NULL;
    FILE *conf_file = fopen(path, "r");
    if (conf_file == NULL) {
        log_error("Error openning config file: %s", path);
        return NULL;
    }
    char value[100];
    char buffer[1024];
    
    bool error = false;
    bool read_next = true;
    for (;!error;) {
        if (read_next) {
            if (NULL == fgets(buffer, 1023, conf_file)) {
                break;
            }
        }
        read_next = true;
        switch (get_line_type(buffer)) {
            case HEADER_START:
            {
                parse_header_value(buffer, value);
                if (strcmp("user", value) == 0) {
                    users[user_index] = read_user_config(conf_file, buffer);
                    if (!users[user_index]) error = true;
                    user_index ++;
                } else if (strcmp("config", value) == 0) {
                    vpnConfig = read_base_config(conf_file, buffer);
                    if (!vpnConfig) error = true;
                } else if (strcmp("routes", value) == 0) {
                    routes = do_read_routes(conf_file, buffer);
                } else {
                    error = true;
                }
                read_next = false;
                break;
            }
        }
    }
    fclose(conf_file);
    if (vpnConfig->mode == CLIENT && check_str(vpnConfig->route_cache_file)) {
        FILE * file = fopen(vpnConfig->route_cache_file, "r");
        if (file != NULL) {
            char tmp[100] = "";
            if (fgets(tmp, 99, file) >= 0) {
                if (tmp == strstr(tmp, "#VER:")) {
                    vpnConfig->route_conf_ver = str2int(trim(tmp + 5));
                }
            }
            fclose(file);
        }
    }
    if (!vpnConfig) vpnConfig = (PVpnConfig) malloc (sizeof(VpnConfig));

    zmalloc(&vpnConfig->users, sizeof(PUserInfo) * (user_index + 1));
    memcpy(vpnConfig->users, users, sizeof(UserInfo *) * (user_index));
    vpnConfig->routes = routes;

    if (error) {
        vpn_config_delete(vpnConfig);
        return NULL;
    }
    return vpnConfig;
}

PRoute * read_routes_cache(const char *path) {
    FILE *file = fopen(path, "r");
    if (file == NULL) return NULL;
    char *buffer = (char *) malloc (sizeof(char) * 1030);
    PRoute *ret = do_read_routes(file, buffer);
    free(buffer);
    fclose(file);
    return ret;
}
